﻿/**************************************************************
 *
 * 唯一标识：6bbf462d-1b0a-4b0c-bf53-06476e502245
 * 命名空间：Sgr.UserAggregate
 * 创建时间：2023/8/6 14:43:17
 * 机器名称：DESKTOP-S0D075D
 * 创建者：antho
 * 电子邮箱：fengqinhua2016@163.com
 * 描述：
 *
 **************************************************************/

using Sgr.Domain.Entities.Auditing;
using Sgr.Domain.Entities;
using Sgr.Exceptions;
using Sgr.Utilities;
using Sgr.Identity.Services;
using Sgr.UPMS.Events;

namespace Sgr.UPMS.Domain.Users
{
    /// <summary>
    /// 用户聚合根
    /// </summary>
    public class User : FullAudited<long, long>, IAggregateRoot, IOptimisticLock, IMustHaveOrg<long>
    {
        #region 私有字段与构造函数

        private List<UserDuty> _duties;
        private List<UserRole> _roles;

        /// <summary>
        /// 用户私有构造函数 - 仅供ORM和内部工厂方法使用
        /// </summary>
        private User()
        {
            _duties = new List<UserDuty>();
            _roles = new List<UserRole>();
        }

        /// <summary>
        /// 创建用户实例
        /// </summary>
        /// <param name="loginName">登录名</param>
        /// <param name="passwordHashService">密码哈希服务</param>
        /// <param name="password">密码</param>
        /// <param name="isAdmin">是否超级管理员</param>
        /// <param name="orgId">组织ID</param>
        internal User(string loginName, IPasswordHashService passwordHashService, string password, bool isAdmin, long orgId) : this()
        {
            Check.StringNotNullOrWhiteSpace(loginName, nameof(loginName));
            Check.StringNotNullOrWhiteSpace(password, nameof(password));
            Check.NotNull(orgId > 0, "组织ID必须大于0");

            LoginName = loginName;
            ChangePassword(passwordHashService, password);
            State = EntityStates.Normal;
            IsSuperAdmin = isAdmin;
            OrgId = orgId;
        }

        #endregion 私有字段与构造函数

        #region 用户基本信息属性

        /// <summary>
        /// 用户姓名
        /// </summary>
        public string? UserName { get; private set; } = string.Empty;

        /// <summary>
        /// 性别
        /// </summary>
        public Gender Gender { get; private set; } = Gender.Unknown;

        /// <summary>
        /// 用户QQ号码
        /// </summary>
        public string? QQ { get; private set; }

        /// <summary>
        /// 出生日期
        /// </summary>
        public DateOnly? BirthDay { get; private set; }

        #endregion 用户基本信息属性

        #region 账号安全

        /// <summary>
        /// 登录名称
        /// </summary>
        public string LoginName { get; private set; } = string.Empty;

        /// <summary>
        /// 登录密码
        /// </summary>
        public string LoginPassword { get; private set; } = string.Empty;

        /// <summary>
        /// 用户绑定的手机号码
        /// </summary>
        public string? UserPhone { get; private set; }

        /// <summary>
        /// 手机号码是否已验证
        /// </summary>
        public bool IsPhoneVerified { get; private set; } = false;

        /// <summary>
        /// 用户绑定的邮箱地址
        /// </summary>
        public string? UserEmail { get; private set; }

        /// <summary>
        /// 邮箱是否已验证
        /// </summary>
        public bool IsEmailVerified { get; private set; } = false;

        /// <summary>
        /// 是否超级管理员
        /// </summary>
        public bool IsSuperAdmin { get; private set; } = false;

        /// <summary>
        /// 账号状态
        /// </summary>
        public EntityStates State { get; private set; } = EntityStates.Normal;

        /// <summary>
        /// 主要身份验证类型
        /// </summary>
        public UserAuthenticationType MainAuthenticationType { get; private set; } = UserAuthenticationType.Password;

        /// <summary>
        /// 微信OpenId(存储用户在微信开放平台中的唯一标识（OpenID）。每个用户在同一个公众号或小程序中都有一个唯一的OpenID)
        /// </summary>
        public string? WeChatOpenId { get; private set; }

        /// <summary>
        /// 微信UnionId( 如果你的系统关联了多个微信开放平台（如多个公众号或小程序），可以使用UnionID来标识同一个用户在不同平台中的唯一性。UnionID是跨应用、跨公众号、跨小程序的唯一标识)
        /// </summary>
        public string? WeChatUnionId { get; private set; }

        /// <summary>
        /// 用户绑定的第三方身份验证器，如Google验证器
        /// </summary>
        public string? AuthenticatorKey { get; private set; }

        /// <summary>
        /// 是否启用双因素认证
        /// </summary>
        public bool TwoFactorEnabled { get; private set; } = false;

        #endregion 账号安全

        #region 登录相关属性

        /// <summary>
        /// 首次登录时间
        /// </summary>
        public DateTime? FirstLoginTime { get; private set; }

        /// <summary>
        /// 最近一次登录时间
        /// </summary>
        public DateTime? LastLoginTime { get; private set; }

        /// <summary>
        /// 登录成功次数
        /// </summary>
        public int LoginSuccessCount { get; private set; } = 0;

        /// <summary>
        /// 连续登录失败次数
        /// </summary>
        public int FailedLoginAttempts { get; private set; } = 0;

        /// <summary>
        /// 禁止登录的截止时间
        /// </summary>
        public DateTime? CannotLoginUntilUtc { get; private set; }

        #endregion 登录相关属性

        #region 组织架构相关属性

        /// <summary>
        /// 所属部门Id
        /// </summary>
        public long? DepartmentId { get; private set; }

        /// <summary>
        /// 所属部门名称
        /// </summary>
        public string DepartmentName { get; private set; } = "";

        /// <summary>
        /// 首要的岗位Id
        /// </summary>
        public long? PrimaryDutyId { get; private set; }

        /// <summary>
        /// 首要的岗位名称
        /// </summary>
        public string PrimaryDeptName { get; private set; } = "";

        /// <summary>
        /// 直接上级用户标识（备用，“上级审批”）
        /// </summary>
        public long? SuperiorUserId { get; set; }

        /// <summary>
        /// 直接上级用户名称（备用，“上级审批”）
        /// </summary>
        public string SuperiorUserName { get; set; } = "";

        #endregion 组织架构相关属性

        #region 导航属性

        /// <summary>
        /// 岗位列表
        /// </summary>
        public IEnumerable<UserDuty> Duties => _duties.AsReadOnly();

        /// <summary>
        /// 角色列表
        /// </summary>
        public IEnumerable<UserRole> Roles => _roles.AsReadOnly();

        #endregion 导航属性

        #region 密码与登录相关方法

        /// <summary>
        /// 登录成功
        /// </summary>
        public void LoginSuccess()
        {
            if (FirstLoginTime == null)
                FirstLoginTime = DateTime.UtcNow;

            LastLoginTime = DateTime.UtcNow;
            LoginSuccessCount += 1;
            FailedLoginAttempts = 0;
        }

        /// <summary>
        /// 登录失败
        /// </summary>
        public void LoginFail()
        {
            FailedLoginAttempts += 1;
        }

        /// <summary>
        /// 禁止登录
        /// </summary>
        /// <param name="cannotLoginUntilUtc">禁止登录的截止时间</param>
        public void LoginLock(DateTime cannotLoginUntilUtc)
        {
            CannotLoginUntilUtc = cannotLoginUntilUtc;
            FailedLoginAttempts = 0;

            NotifyUserAuthChanged();
        }

        /// <summary>
        /// 永久禁止登录
        /// <para>情况1：当组织机构被注销后，相关账户会被永久禁止登录</para>
        /// </summary>
        public void LoginLockForever()
        {
            LoginLock(DateTime.MaxValue);
        }

        /// <summary>
        /// 恢复登录权限
        /// </summary>
        public void LoginRestore()
        {
            CannotLoginUntilUtc = null;
            NotifyUserAuthChanged();
        }

        /// <summary>
        /// 校验密码是否正确
        /// </summary>
        /// <param name="service">密码哈希服务</param>
        /// <param name="password">待验证密码</param>
        /// <returns>密码是否匹配</returns>
        public bool CheckPassword(IPasswordHashService service, string password)
        {
            Check.NotNull(service, nameof(service));
            return service.VerifyPassword(password, LoginPassword);
        }

        /// <summary>
        /// 修改密码
        /// </summary>
        /// <param name="service">密码哈希服务</param>
        /// <param name="password">新密码</param>
        /// <exception cref="DomainException">密码为空时抛出异常</exception>
        public void ChangePassword(IPasswordHashService service, string password)
        {
            Check.NotNull(service, nameof(service));

            if (string.IsNullOrEmpty(password))
                throw DomainException.ValidationError("密码不可以为空！");

            LoginPassword = service.HashPassword(password);
        }

        /// <summary>
        /// 检查用户密码是否允许重置
        /// </summary>
        /// <returns></returns>
        public DomainRuleCheckResult CanChangePassword()
        {
            if (this.OrgId == Constant.SUPER_ADMIN_ORG_ID && this.IsSuperAdmin)
            {
                return DomainRuleCheckResult.Fail("系统运维组织下的超管用户不允许重置密码!");
            }

            return DomainRuleCheckResult.Ok();
        }

        #endregion 密码与登录相关方法

        #region 用户信息更新方法

        /// <summary>
        /// 设置用户姓名
        /// </summary>
        /// <param name="userName">姓名</param>
        /// <param name="gender">性别</param>
        /// <param name="qq">QQ号码</param>
        /// <param name="dirthDay">出生日期</param>
        public void SetUserInfo(string? userName, Gender gender = Gender.Unknown, string? qq = null, DateOnly? dirthDay = null)
        {
            UserName = userName;
            Gender = gender;
            QQ = qq;
            BirthDay = dirthDay;
        }

        /// <summary>
        /// 绑定手机号码
        /// </summary>
        /// <param name="phoneNumber">手机号码</param>
        /// <param name="isVerified">是否已验证</param>
        public void BindPhoneNumber(string phoneNumber, bool isVerified = false)
        {
            if (string.IsNullOrWhiteSpace(phoneNumber))
                throw DomainException.ValidationError("手机号不能为空");

            // 可以在此添加手机号格式验证
            UserPhone = phoneNumber;
            IsPhoneVerified = isVerified;

            // 如果这是首次绑定手机并且验证通过，同时更新主认证方式
            if (isVerified && MainAuthenticationType == UserAuthenticationType.Password &&
               string.IsNullOrWhiteSpace(WeChatOpenId))
            {
                MainAuthenticationType = UserAuthenticationType.PhoneNumber;
            }

            // 如果是已验证的手机绑定，需要触发用户信息变更事件
            if (isVerified)
                NotifyUserAuthChanged();
        }

        /// <summary>
        /// 绑定微信账号
        /// </summary>
        /// <param name="openId">微信OpenId</param>
        /// <param name="unionId">微信UnionId(可选)</param>
        public void BindWeChat(string openId, string? unionId = null)
        {
            if (string.IsNullOrWhiteSpace(openId))
                throw DomainException.ValidationError("微信OpenId不能为空");

            WeChatOpenId = openId;
            WeChatUnionId = unionId;

            // 如果这是首次绑定微信，同时更新主认证方式
            if (MainAuthenticationType == UserAuthenticationType.Password &&
               (!IsPhoneVerified || string.IsNullOrWhiteSpace(UserPhone)))
            {
                MainAuthenticationType = UserAuthenticationType.WeChat;
            }

            NotifyUserAuthChanged();
        }

        /// <summary>
        /// 绑定邮箱
        /// </summary>
        /// <param name="email">邮箱地址</param>
        /// <param name="isVerified">是否已验证</param>
        public void BindUserEmail(string? email, bool isVerified = false)
        {
            UserEmail = email == null ? "" : email.Trim().ToLowerInvariant();
            IsEmailVerified = isVerified;

            if (isVerified)
                NotifyUserAuthChanged();
        }

        /// <summary>
        /// 设置双因素认证
        /// </summary>
        /// <param name="enabled">是否启用</param>
        /// <param name="authenticatorKey">验证器密钥(启用时必填)</param>
        public void SetTwoFactorAuthentication(bool enabled, string? authenticatorKey = null)
        {
            if (enabled && string.IsNullOrWhiteSpace(authenticatorKey))
                throw DomainException.ValidationError("启用双因素认证时必须提供验证器密钥");

            TwoFactorEnabled = enabled;
            if (enabled)
                AuthenticatorKey = authenticatorKey;
            else
                AuthenticatorKey = null;

            NotifyUserAuthChanged();
        }

        /// <summary>
        /// 设置所属部门
        /// </summary>
        /// <param name="departmentId">部门ID</param>
        /// <param name="departmentName">部门名称</param>
        public void SetDepartment(long? departmentId, string departmentName)
        {
            if (IsSuperAdmin && departmentId.HasValue)
                throw DomainException.ValidationError("超级管理员不可以绑定部门信息！");

            DepartmentId = departmentId;
            DepartmentName = departmentName ?? string.Empty;
        }

        /// <summary>
        /// 设置首要岗位
        /// </summary>
        /// <param name="primaryDutyId">岗位ID</param>
        /// <param name="primaryDutyName">岗位名称</param>
        public void SetPrimaryDuty(long? primaryDutyId, string primaryDutyName)
        {
            if (IsSuperAdmin && primaryDutyId.HasValue)
                throw DomainException.ValidationError("超级管理员不可以绑定岗位信息！");

            PrimaryDutyId = primaryDutyId;
            PrimaryDeptName = primaryDutyName ?? string.Empty;
        }

        /// <summary>
        /// 设置直接上级
        /// </summary>
        /// <param name="superiorUserId">直接上级ID</param>
        /// <param name="superiorUserName">直接上级名称</param>
        public void SetSuperiorUser(long? superiorUserId, string superiorUserName)
        {
            if (IsSuperAdmin && superiorUserId.HasValue)
                throw DomainException.ValidationError("超级管理员不可以绑定直接上级信息！");

            SuperiorUserId = superiorUserId;
            SuperiorUserName = superiorUserName ?? string.Empty;
        }

        /// <summary>
        /// 差异更新用户岗位列表(拥有多个岗位)
        /// </summary>
        /// <param name="dutyIds">岗位ID列表</param>
        public void DifferenceUpdateDuties(long[] dutyIds)
        {
            if (IsSuperAdmin)
                throw DomainException.ValidationError("超级管理员不可以绑定岗位信息！");

            Check.NotNull(dutyIds, nameof(dutyIds));
            CollectionSyncHelper.SyncCollection(_duties, dutyIds, id => new UserDuty(id), d => d.DutyId);

            // 更新岗位也需要触发用户权限变更事件
            NotifyUserAuthChanged();
        }

        /// <summary>
        /// 差异更新用户角色列表(拥有多个角色)
        /// </summary>
        /// <param name="roleIds">角色ID列表</param>
        /// <param name="isCreateAdminOperate">是否为创建管理操作</param>
        public void DifferenceUpdateRoles(long[] roleIds, bool isCreateAdminOperate = false)
        {
            Check.NotNull(roleIds, nameof(roleIds));

            //如果该操作并非在创建用户时调用，则不允许修改超级管理员的角色
            if (!isCreateAdminOperate && IsSuperAdmin)
                throw DomainException.ValidationError("超级管理员不可以修改角色！");

            CollectionSyncHelper.SyncCollection(_roles, roleIds, id => new UserRole(id), r => r.RoleId);

            NotifyUserAuthChanged();
        }

        /// <summary>
        /// 调整用户状态
        /// </summary>
        /// <param name="state">目标状态</param>
        /// <exception cref="DomainException">超级管理员不能被禁用</exception>
        public void ChangeEntityStates(EntityStates state)
        {
            if (IsSuperAdmin && state == EntityStates.Deactivate)
                throw DomainException.ValidationError("超级管理员不可以禁用！");

            if (this.State != state)
            {
                this.State = state;
                NotifyUserAuthChanged();
            }
        }

        #endregion 用户信息更新方法

        #region 辅助方法

        /// <summary>
        /// 检查用户账号状态
        /// </summary>
        /// <returns>用户状态枚举</returns>
        public UserState GetUserState()
        {
            if (this.IsDeleted)
                return UserState.Deleted;

            if (this.State != EntityStates.Normal)
                return UserState.Disabled;

            if (this.CannotLoginUntilUtc.HasValue && this.CannotLoginUntilUtc.Value > DateTime.UtcNow)
                return UserState.Locked;

            return UserState.Normal;
        }

        /// <summary>
        /// 标记实体待删除
        /// </summary>
        public void MarkForDeletion()
        {
            if (IsSuperAdmin)
                throw DomainException.ValidationError("超级管理员不可以删除！");

            // 清理手机相关信息
            UserPhone = null;
            IsPhoneVerified = false;

            // 清理邮箱相关信息
            UserEmail = null;
            IsEmailVerified = false;

            // 清理微信相关信息
            WeChatOpenId = null;
            WeChatUnionId = null;

            // 清理双因素认证相关信息
            TwoFactorEnabled = false;
            AuthenticatorKey = null;

            // 清理QQ信息
            QQ = null;
            // 清理出生日期
            BirthDay = null;

            // 重置主要身份验证类型为密码登录
            MainAuthenticationType = UserAuthenticationType.Password;

            NotifyUserAuthChanged();
        }

        /// <summary>
        /// 发送用户权限变更事件
        /// </summary>
        private void NotifyUserAuthChanged()
        {
            this.AddDomainEvent(new UserAuthChangedDomainEvent(this.OrgId, this.Id));
        }

        /// <summary>
        /// 验证手机号码
        /// </summary>
        public void VerifyPhoneNumber()
        {
            if (string.IsNullOrWhiteSpace(UserPhone))
                throw DomainException.ValidationError("未绑定手机号，无法验证");

            IsPhoneVerified = true;
            NotifyUserAuthChanged();
        }

        /// <summary>
        /// 验证邮箱
        /// </summary>
        public void VerifyEmail()
        {
            if (string.IsNullOrWhiteSpace(UserEmail))
                throw DomainException.ValidationError("未绑定邮箱，无法验证");

            IsEmailVerified = true;
            NotifyUserAuthChanged();
        }

        #endregion 辅助方法

        #region 接口实现

        /// <summary>
        /// 行版本 - 实现乐观锁
        /// </summary>
        public long RowVersion { get; set; }

        /// <summary>
        /// 所属组织ID - 实现IMustHaveOrg接口
        /// </summary>
        public long OrgId { get; private set; }

        #endregion 接口实现
    }
}